package com.dmz.etc.server.netty.server;

import com.dmz.etc.server.config.DmzProp;
import com.dmz.etc.server.config.constant.DmzConfig;
import com.dmz.etc.server.domain.MsgClazz;
import com.dmz.etc.server.netty.attr.ChannelAttrs;
import com.dmz.etc.server.netty.attr.ChannelMatchMap;
import com.dmz.etc.server.netty.client.NettyClient;
import com.dmz.etc.server.service.ServerService;
import com.dmz.etc.server.utils.Convert;
import com.dmz.etc.server.utils.SpringUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * Socket拦截器，用于处理客户端的行为
 *
 * @author lee
 **/
@Slf4j
public class ServerHandler extends ChannelInboundHandlerAdapter {


    private NioEventLoopGroup workerGroup;

    public ServerHandler(NioEventLoopGroup workerGroup) {
        this.workerGroup = workerGroup;
    }

    DmzProp dmzProp = SpringUtil.getBean(DmzProp.class);

    public static final ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    // 假设我们知道完整消息的长度，或者消息有一个长度前缀
    private static int MESSAGE_LENGTH = 0;
    private ByteBuf cumulativeBuffer = null;

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        //通过IP判断服务接收到的报文来源，确定报文走向
        selectChannel(ctx);
    }

    /**
     * 读取到客户端发来的消息
     *
     * @param ctx ChannelHandlerContext
     * @param msg msg
     * @throws Exception e
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        byte[] data = (byte[]) msg;
        ByteBuf in = Unpooled.wrappedBuffer(data);
        int serverForward = ctx.channel().attr(ChannelAttrs.forward).get();
        log.info("3开始获取报文============================forward:{}", serverForward);
        try {
            if (cumulativeBuffer == null || cumulativeBuffer.readableBytes() <= 0) {
                byte[] len = new byte[4];
                System.arraycopy(data, 0, len, 0, len.length);
                if (serverForward == DmzConfig.FORWARD_TO_BANK) {
                    MESSAGE_LENGTH = Convert.bytes2Int(len);
                } else {
                    MESSAGE_LENGTH = Integer.parseInt(new String(len)) + 4;
                }
                cumulativeBuffer = ctx.alloc().buffer(MESSAGE_LENGTH);
            }
            // 将接收到的数据写入cumulativeBuffer
            cumulativeBuffer.writeBytes(in);
            // 检查是否接收到了完整的消息
            if (cumulativeBuffer.readableBytes() >= MESSAGE_LENGTH) {
                // 读取完整的消息
                ByteBuf message = cumulativeBuffer.readBytes(MESSAGE_LENGTH);
                // 在这里处理完整的消息
                data = new byte[message.readableBytes()];
                message.readBytes(data);
                log.info("4接收到报文，forward:{}============报文长度:{}", serverForward, data.length);
                log.info("4.1原始报文内容:{}", data);
                process(ctx, data);
                // 重置cumulativeBuffer，以便接收下一条消息
                cumulativeBuffer.clear();
            }
        } finally {
            // 释放接收到的ByteBuf
            in.release();
        }
    }


    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        //log.info("新的客户端链接：{}", ctx.channel().id().asShortText());
        clients.add(ctx.channel());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {

        //log.info("远程客户端断开链接：{}", ctx.channel().id().asShortText());
        closeChannels(ctx);
        clients.remove(ctx.channel());
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
        String socketString = ctx.channel().remoteAddress().toString();
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.READER_IDLE) {
                log.info("Client:{},READER_IDLE", socketString);
                Channel channel = ctx.channel();
                // 超时未收到心跳包，更新设备状态为离线
                // todo 更新设备状态
                ctx.disconnect();
                clients.remove(channel);
            } else if (event.state() == IdleState.WRITER_IDLE) {
                log.info("Client:{}, WRITER_IDLE", socketString);
                ctx.disconnect();
                Channel channel = ctx.channel();
                clients.remove(channel);
            } else if (event.state() == IdleState.ALL_IDLE) {
                log.info("Client:{},ALL_IDLE", socketString);
                Channel channel = ctx.channel();
                // 超时未收到心跳包，更新设备状态为离线
                ctx.disconnect();
                clients.remove(channel);
            }
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error("client disconnected error：{}, msg：{}", ctx.channel().id(), cause.getMessage(), cause);
        closeChannels(ctx);
        ctx.channel().close();
        clients.remove(ctx.channel());
    }

    private void selectChannel(ChannelHandlerContext ctx) throws Exception {

        String bankHosts = dmzProp.getBankHosts();
        String etcHosts = dmzProp.getEtcHosts();

        List<String> bHosts = Arrays.asList(Objects.requireNonNull(bankHosts).split(","));
        List<String> eHosts = Arrays.asList(Objects.requireNonNull(etcHosts).split(","));
        // 获取远程地址（客户端地址）
        InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
        if(bHosts.contains(address.getHostString()) || eHosts.contains(address.getHostString())) {
            log.info("1监听到IP=={}==连接服务===================================================", address.getAddress().getHostAddress());
        }
        if (bHosts.contains(address.getHostString()) || eHosts.contains(address.getHostString())) {
            super.channelActive(ctx);
            if (eHosts.contains(address.getHostString())) {
                //serversA.add(ctx.channel());
                ctx.channel().attr(ChannelAttrs.forward).set(DmzConfig.FORWARD_TO_BANK);
                log.info("2判断是来自ETC的报文请求，需解密并转发给CSP == forward:{}", DmzConfig.FORWARD_TO_BANK);
            } else {
                if (bHosts.contains(address.getHostString())) {
                    //serversC.add(ctx.channel());
                    ctx.channel().attr(ChannelAttrs.forward).set(DmzConfig.FORWARD_TO_ETC);
                    log.info("2判断是来自CSP的报文请求，需加密并转发给ETC == forward:{}", DmzConfig.FORWARD_TO_ETC);
                }
            }
        }
    }

    private Channel initClient(ChannelHandlerContext ctx) {
        int forward = ctx.channel().attr(ChannelAttrs.forward).get();
        if (ChannelMatchMap.getClientChannel(ctx.channel().id()) == null) {
            List<String> hosts;
            List<String> ports;
            if (forward == DmzConfig.FORWARD_TO_BANK) {
                String bankHosts = dmzProp.getBankHosts();
                hosts = Arrays.asList(Objects.requireNonNull(bankHosts).split(","));
                String bankPorts = dmzProp.getBankPorts();
                ports = Arrays.asList(Objects.requireNonNull(bankPorts.split(",")));
                log.info("7.1注册连接到CSP的客户端====================");
            } else {
                //连接到大端的服务端
                String etcHosts = dmzProp.getEtcHosts();
                hosts = Arrays.asList(Objects.requireNonNull(etcHosts).split(","));
                String etcPorts = dmzProp.getEtcPorts();
                ports = Arrays.asList(Objects.requireNonNull(etcPorts.split(",")));
                log.info("7.1注册连接到ETC的客户端=====================");
            }
            for (int i = 0; i < hosts.size(); i++) {
                try {
                    ctx.channel().attr(ChannelAttrs.address).set(new ChannelAttrs.Address(hosts.get(i), Integer.parseInt(ports.get(i))));
                    log.info("注册客户端信息：ip:{},port:{}", hosts.get(i), ports.get(i));
                    return initChannel(ctx);
                } catch (Exception e) {
                    if (i > 0 && i == hosts.size() - 1) {
                        return null;
                    }
                    log.error("server client start error", e);
                }
            }
            return null;
        } else {
            log.info("已注册过该客户端,使用已注册的客户端即可，客户端ID：{}", ctx.channel().id());
            return ChannelMatchMap.getClientChannel(ctx.channel().id());
        }

    }

    private Channel initChannel(ChannelHandlerContext ctx) throws Exception {
        return new NettyClient().start(workerGroup, ctx);
    }

    private void closeChannels(ChannelHandlerContext ctx) {
        try {
            Channel c = ChannelMatchMap.getClientChannel(ctx.channel().id());
            if (c != null) {
                log.info("server {} close client {}", ctx.channel().id(), c.id());
                c.closeFuture().sync();
                c.close();
                ChannelMatchMap.removeChannel(ctx.channel().id());
            }
        } catch (Exception e) {
            log.error("channel close error", e);
        }
    }

    private void process(ChannelHandlerContext ctx, byte[] data) throws Exception {
        int serverForward = ctx.channel().attr(ChannelAttrs.forward).get();
        log.info("5解析报文==================================forward:{}", serverForward);
        try {
            ServerService serverService = SpringUtil.getBean(ServerService.class);
            //从etc到csp的业务报文
            if (serverForward == DmzConfig.FORWARD_TO_BANK) {

                MsgClazz msgClazz = serverService.forward(data, ctx.channel());
                //转发消息
                if (msgClazz.isToc()) {
                    log.info("7注册客户端转发报文==================================forward:{}", serverForward);
                    Channel c = initClient(ctx);
                    if (c != null && c.isActive()) {
                        c.writeAndFlush(msgClazz.getDataStr());
                        log.info("8转发报文成功================================:{}", msgClazz.getDataStr());
                    } else {
                        log.info("客户端注册失败");
                        closeChannels(ctx);

                    }
                } else {
                    //直接回复
                    log.info("7直接回复报文==========================================");
                    ctx.writeAndFlush(msgClazz.getData());
                    log.info("回复成功");
                }
            } else {
                //从csp到etc的业务报文
                if (serverForward == DmzConfig.FORWARD_TO_ETC) {
                    MsgClazz msgClazz = serverService.encrypt(data, ctx.channel());
                    Channel c = initClient(ctx);
                    log.info("7注册客户端转发报文==================================forward:{}", serverForward);
                    if (c != null && c.isActive()) {
                        c.writeAndFlush(msgClazz.getData());
                        log.info("8转发报文成功================================:{}", msgClazz.getDataStr());
                    }
                }
            }
        } catch (Exception e) {
            log.error("server forward error", e);
            closeChannels(ctx);
            throw e;
        } finally {
            log.info("9回复完毕=================={}==================================", serverForward);
        }
    }
}